package com.example.glideprogressiveloadimg.util;

import android.media.ExifInterface;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

public class JfifUtil {

    /**
     * Definitions of jpeg markers as well as overall description of jpeg file format can be found
     * here: <a href="http://www.w3.org/Graphics/JPEG/itu-t81.pdf">Recommendation T.81</a>
     */
    public static final int MARKER_FIRST_BYTE = 0xFF;  //标记的起始字节

    public static final int MARKER_ESCAPE_BYTE = 0x00; //填充数据，需要跳过
    public static final int MARKER_SOI = 0xD8;   //标记文件开头
    public static final int MARKER_EOI = 0xD9;   //标记文件结尾

    public static final int MARKER_SOFn = 0xC0;  //表示一个基线JPEG，普通jpeg
    public static final int MARKER_TEM = 0x01;   //

    public static final int MARKER_SOS = 0xDA;   //渐进式jpeg文件中，多个扫描位置

    public static final int MARKER_APP1 = 0xE1;  //TIFF格式数据， metadata数据
    public static final int MARKER_RST0 = 0xD0;   //DRI标记， r宏块,  r是DRI标记设置的重启间隔，
    public static final int MARKER_RST7 = 0xD7;   //DRI标记， r宏块,  r是DRI标记设置的重启间隔，

    public static final int APP1_EXIF_MAGIC = 0x45786966;  //

    private JfifUtil() {}

    /**
     * Determines auto-rotate angle based on orientation information.
     *
     * @param orientation orientation information, one of {1, 3, 6, 8}.
     * @return orientation: 1/3/6/8 -> 0/180/90/270.
     */
    public static int getAutoRotateAngleFromOrientation(int orientation) {
        return TiffUtil.getAutoRotateAngleFromOrientation(orientation);
    }

    /**
     * Gets orientation information from jpeg byte array.
     *
     * @param jpeg the input byte array of jpeg image
     * @return orientation: 1/8/3/6. Returns 0 if there is no valid orientation information.
     */
    public static int getOrientation(byte[] jpeg) {
        // wrapping with ByteArrayInputStream is cheap and we don't have duplicate implementation
        return getOrientation(new ByteArrayInputStream(jpeg));
    }

    /**
     * Get orientation information from jpeg input stream.
     * 从图片的输入流中获取 方向信息
     *
     * @param is the input stream of jpeg image
     * @return orientation: 1/8/3/6. Returns {@value
     *     android.media.ExifInterface#ORIENTATION_UNDEFINED} if there is no valid orientation
     *     information.
     */
    public static int getOrientation(InputStream is) {
        try {
            int length = moveToAPP1EXIF(is);
            if (length == 0) {
                return ExifInterface.ORIENTATION_UNDEFINED;
            }
            return TiffUtil.readOrientationFromTIFF(is, length);
        } catch (IOException ioe) {
            return ExifInterface.ORIENTATION_UNDEFINED;
        }
    }

    /**
     * 读取输入流的内容，直到找到指定的标记。
     * 标记将被消耗，输入流将定位在指定标记之后。
     *
     * Reads the content of the input stream until specified marker is found. Marker will be consumed
     * and the input stream will be positioned after the specified marker.
     *
     * @param is the input stream to read bytes from
     * @param markerToFind the marker we are looking for
     * @return boolean: whether or not we found the expected marker from input stream.
     */
    public static boolean moveToMarker(InputStream is, int markerToFind) throws IOException {
//        Preconditions.checkNotNull(is);
        if (is == null) return false;
        // ISO/IEC 10918-1:1993(E)
        while (StreamProcessor.readPackedInt(is, 1, false) == MARKER_FIRST_BYTE) {
            int marker = MARKER_FIRST_BYTE;
            while (marker == MARKER_FIRST_BYTE) {   //读取FF字节之后的下一个字节， 如果是连续FF，则跳过
                marker = StreamProcessor.readPackedInt(is, 1, false);
            }

            if (markerToFind == MARKER_SOFn && isSOFn(marker)) {  //表示是JPEG图片的类型，直接返回
                return true;
            }
            if (marker == markerToFind) {  //找到指定位置
                return true;
            }

            // Check if the marker is SOI or TEM. These two don't have length field, so we skip it.
            // 检查标记是否为SOI或TEM。这两个没有长度字段，所以我们跳过它。
            if (marker == MARKER_SOI || marker == MARKER_TEM) {
                continue;
            }

            // Check if the marker is EOI or SOS. We will stop reading since metadata markers don't
            // come after these two markers.
            // 检查标记是否为EOI或SOS。
            // 我们将停止读取，因为元数据标记没有出现在这两个标记之后。
            if (marker == MARKER_EOI || marker == MARKER_SOS) {
                return false;
            }

            // read block length
            // subtract 2 as length contain SIZE field we just read
            // 读取块长度减去2，因为长度包含我们刚刚读取的大小字段
            int length = StreamProcessor.readPackedInt(is, 2, false) - 2;
            // Skip other markers.
            is.skip(length);
        }
        return false;
    }

    private static boolean isSOFn(int marker) {
        // There are no SOF4, SOF8, SOF12
        switch (marker) {
            case 0xC0:
            case 0xC1:
            case 0xC2:
            case 0xC3:
            case 0xC5:
            case 0xC6:
            case 0xC7:
            case 0xC9:
            case 0xCA:
            case 0xCB:
            case 0xCD:
            case 0xCE:
            case 0xCF:
                return true;
            default:
                return false;
        }
    }

    /**
     * Positions the given input stream to the beginning of the EXIF data in the JPEG APP1 block.
     * 将给定的输入流定位到JPEG APP1块中的EXIF数据的开头。
     *
     * @param is the input stream of jpeg image
     * @return length of EXIF data  返回EXIF数据块的长度
     */
    private static int moveToAPP1EXIF(InputStream is) throws IOException {
        if (moveToMarker(is, MARKER_APP1)) { // 输入流 指向 TIFF格式数据， metadata数据 的开始位置
            // read block length
            // subtract 2 as length contain SIZE field we just read
            int length = StreamProcessor.readPackedInt(is, 2, false) - 2;
            if (length > 6) {
                int magic = StreamProcessor.readPackedInt(is, 4, false);
                length -= 4;
                int zero = StreamProcessor.readPackedInt(is, 2, false);
                length -= 2;
                if (magic == APP1_EXIF_MAGIC && zero == 0) {
                    // JEITA CP-3451 Exif Version 2.2
                    return length;
                }
            }
        }
        return 0;
    }
}
